using System;
using System.Linq;
using System.Reflection;
using UnityEngine;

public static class Utils
{
    /// <summary>
    /// Returns a Vector3 object of which every component is the absolute value of that of the given value.
    /// </summary>
    public static Vector3 Abs(Vector3 value)
    {
        return new Vector3(Mathf.Abs(value.x), Mathf.Abs(value.y), Mathf.Abs(value.z));
    }

    /// <summary>
    /// Returns if every component of a Vector3 object is smaller than that of the other.
    /// </summary>
    public static bool IsSmallerThan(this Vector3 self, Vector3 other)
    {
        return self.x < other.x && self.y < other.y && self.z < other.z;
    }

    /// <summary>
    /// Returns if every component of a Vector3 object is smaller than or equal to that of the other.
    /// </summary>
    public static bool IsSmallerOrEqual(this Vector3 self, Vector3 other)
    {
        return self.x <= other.x && self.y <= other.y && self.z <= other.z;
    }

    /// <summary>
    /// Returns if every component of a Vector3 object is greater than that of the other.
    /// </summary>
    public static bool IsGreater(this Vector3 self, Vector3 other)
    {
        return self.x > other.x && self.y > other.y && self.z > other.z;
    }

    /// <summary>
    /// Returns if every component of a Vector3 object is greater than or equal to that of the other.
    /// </summary>
    public static bool IsGreaterOrEqual(this Vector3 self, Vector3 other)
    {
        return self.x >= other.x && self.y >= other.y && self.z >= other.z;
    }

    /// <summary>
    /// Returns a Vector3 object of which every component is the result of that of two given objects multiplied.
    /// </summary>
    public static Vector3 Multiply(this Vector3 self, Vector3 other)
    {
        return new Vector3(self.x * other.x, self.y * other.y, self.z * other.z);
    }

    /// <summary>
    /// Returns a Vector3 object of which every component is the result of that of two given objects divided.
    /// </summary>
    public static Vector3 Divide(this Vector3 self, Vector3 other)
    {
        return new Vector3(self.x / other.x, self.y / other.y, self.z / other.z);
    }

    public static bool IsAllTrue(this Func<bool> func)
    {
        return func == null || func.GetInvocationList().All(del => !(!(del as Func<bool>)?.Invoke() ?? true));
    }

    public static bool IsAllTrue<T>(this Predicate<T> predicate, T val)
    {
        return predicate == null ||
               predicate.GetInvocationList().All(del => !(!(del as Predicate<T>)?.Invoke(val) ?? true));
    }

    public static Type[] FindAllTypeByInterface(Type type)
    {
        return !type.IsInterface
            ? null
            : AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces().Contains(type)))
                .ToArray();
        ;
    }

    public static Type[] FindAllTypeByInterface(Type type, params Type[] restrictions)
    {
        return !type.IsInterface
            ? null
            : AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(a =>
                    a.GetTypes().Where(t => t.GetInterfaces().Contains(type) && restrictions.All(t.IsSubclassOf)))
                .ToArray();
    }

    public static T DeepCopy<T>(this T obj) 
    {
        if (obj == null) return default;
        Type type = obj.GetType();
        if (type == typeof(string) || type.IsValueType || type.IsSubclassOf(typeof(MonoBehaviour)))
        {
            Debug.Log($"{type.Name} is ValueType");
            return obj;
        }

        var copy = Activator.CreateInstance<T>();
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
                                    BindingFlags.Static);
        foreach (FieldInfo field in fields)
        {
            try
            {
                field.SetValue(copy, field.GetValue(obj));
            }
            catch
            {
                //...
            }
        }

        return copy;
    }
}